package top.xierongsheng.code.aspect.log;

import com.alibaba.fastjson.JSON;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import top.xierongsheng.user.model.SysLog;
import top.xierongsheng.user.service.ILogService;
import top.xierongsheng.utlis.data.RandomUtil;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 日志切面用于收集操作日志
 *
 * @auther 谢荣生
 * @datetime 2020/11/20 9:06
 */
@Slf4j
@Aspect
@Component
public class RequestLogAspect {

    @Resource
    private ILogService logService;


    /**
     * 在所有有注解标记( @Log )的方法中切入
     *
     * @auther 谢荣生
     * @datetime 2020/11/20 9:08
     */
    @Pointcut("@annotation(log)")
    public void requestServer(Log log) {
    }

    @Around("requestServer(log)")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint, Log log) throws Throwable {
        long start = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        Object result = proceedingJoinPoint.proceed();
        //记录日志
        SysLog sysLog = new SysLog();
        sysLog.setId(RandomUtil.getUUID());
        sysLog.setType(log.type().name());
        sysLog.setOperationId(request.getHeader("userId"));
        sysLog.setOperationDescribe(log.desc());
        sysLog.setOperationModule(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
                proceedingJoinPoint.getSignature().getName()));
        sysLog.setOperationTime(new Date());
        sysLog.setOperationTakeUpTime(System.currentTimeMillis() - start);
        sysLog.setRequestIp(getIp(request));
        sysLog.setRequestMethod(request.getMethod());
        sysLog.setRequestUrl(request.getRequestURL().toString());
        sysLog.setRequestData(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint).toString());
        sysLog.setResult(JSON.toJSONString(result));
        logService.save(sysLog);
        return result;
    }


    @AfterThrowing(pointcut = "requestServer(log)", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e, Log log) {
        long start = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        //记录日志
        SysLog sysLog = new SysLog();
        sysLog.setId(RandomUtil.getUUID());
        sysLog.setType(log.type().name());
        sysLog.setOperationId(request.getHeader("userId"));
        sysLog.setOperationDescribe(log.desc());
        sysLog.setOperationModule(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName()));
        sysLog.setOperationTime(new Date());
        sysLog.setOperationTakeUpTime(System.currentTimeMillis() - start);
        sysLog.setRequestIp(getIp(request));
        sysLog.setRequestMethod(request.getMethod());
        sysLog.setRequestUrl(request.getRequestURL().toString());
        sysLog.setRequestData(getRequestParamsByJoinPoint(joinPoint).toString());
        sysLog.setResult(JSON.toJSONString(e));
        logService.save(sysLog);
    }

    /**
     * 获取入参
     *
     * @param proceedingJoinPoint
     * @return
     */
    private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = joinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
        Map<String, Object> requestParams = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];

            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                value = file.getOriginalFilename();  //获取文件名
            }

            requestParams.put(paramNames[i], value);
        }

        return requestParams;
    }

    private String getIp(HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
